还在用 HttpUtil?全新 HTTP 客户端来了
The following article is from macrozheng Author 梦想de星空
我们平时开发项目的时候,经常会需要远程调用下其他服务提供的接口,于是我们会使用一些 HTTP 工具类比如 Hutool 提供的 HttpUtil。SpringBoot 3.0 出了一个
Http Interface
的新特性,它允许我们使用声明式服务调用的方式来调用远程接口,今天我们就来聊聊它的使用!
简介
Http Interface
让你可以像定义 Java 接口那样定义 HTTP 服务,而且用法和你平时写 Controller 中方法完全一致。它会为这些 HTTP 服务接口自动生成代理实现类,底层是基于 Webflux 的 WebClient 实现的。
使用声明式服务调用确实够优雅,下面是一段使用Http Interface
声明的Http服务代码。
使用
在 SpringBoot 3.0 中使用
Http Interface
是非常简单的,下面我们就来体验下。
依赖集成
首先在项目的 pom.xml
中定义好 SpringBoot 的版本为3.0.0
;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
由于 SpringBoot 最低要求为 Java 17
,我们需要先安装好 JDK 17,安装完成后配置项目的 SDK 版本为Java 17
,JDK 下载地址:https://www.oracle.com/cn/java/technologies/downloads/
由于 Http Interface
需要依赖 webflux 来实现,我们还需添加它的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
基本使用
下面,我们来体验下
Http Interface
的基本使用。
首先我们准备一个服务来方便远程调用,打开 Swagger 看下,里面有一个登录接口和需要登录认证的商品品牌 CRUD 接口
先在 application.yml
中配置好服务地址;
remote:
baseUrl: http://localhost:8088/
再通过 @HttpExchange
声明一个 Http 服务,使用@PostExchange
注解表示进行 POST 请求;
/**
* 定义Http接口,用于调用远程的UmsAdmin服务
* Created by macro on 2022/1/19.
*/
@HttpExchange
public interface UmsAdminApi {
@PostExchange("admin/login")
CommonResult<LoginInfo> login(@RequestParam("username") String username, @RequestParam("password") String password);
}
再创建一个远程调用品牌服务的接口,参数注解使用我们平时写Controller 方法用的那些即可;
/**
* 定义Http接口,用于调用远程的PmsBrand服务
* Created by macro on 2022/1/19.
*/
@HttpExchange
public interface PmsBrandApi {
@GetExchange("brand/list")
CommonResult<CommonPage<PmsBrand>> list(@RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize);
@GetExchange("brand/{id}")
CommonResult<PmsBrand> detail(@PathVariable("id") Long id);
@PostExchange("brand/create")
CommonResult create(@RequestBody PmsBrand pmsBrand);
@PostExchange("brand/update/{id}")
CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand);
@GetExchange("brand/delete/{id}")
CommonResult delete(@PathVariable("id") Long id);
}
为方便后续调用需要登录认证的接口,我创建了 TokenHolder
这个类,把 token 存储到了 Session 中;
/**
* 登录token存储(在Session中)
* Created by macro on 2022/1/19.
*/
@Component
public class TokenHolder {
/**
* 添加token
*/
public void putToken(String token) {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
request.getSession().setAttribute("token", token);
}
/**
* 获取token
*/
public String getToken() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
Object token = request.getSession().getAttribute("token");
if(token!=null){
return (String) token;
}
return null;
}
}
创建 Java 配置,配置好请求用的客户端 WebClient 及 Http 服务对象即可,由于品牌服务需要添加认证头才能正常访问,所以使用了过滤器进行统一添加;
@Configuration
public class HttpInterfaceConfig {
@Value("${remote.baseUrl}")
private String baseUrl;
@Autowired
private TokenHolder tokenHolder;
@Bean
WebClient webClient() {
return WebClient.builder()
//添加全局默认请求头
.defaultHeader("source", "http-interface")
//给请求添加过滤器,添加自定义的认证头
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("Authorization", tokenHolder.getToken())
.build();
return next.exchange(filtered);
})
.baseUrl(baseUrl).build();
}
@Bean
UmsAdminApi umsAdminApi(WebClient client) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(UmsAdminApi.class);
}
@Bean
PmsBrandApi pmsBrandApi(WebClient client) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(PmsBrandApi.class);
}
}
接下来在 Controller 中注入 Http 服务对象,然后进行调用即可;
/**
* HttpInterface测试接口
* Created by macro on 2022/1/19.
*/
@RestController
@Api(tags = "HttpInterfaceController")
@Tag(name = "HttpInterfaceController", description = "HttpInterface测试接口")
@RequestMapping("/remote")
public class HttpInterfaceController {
@Autowired
private UmsAdminApi umsAdminApi;
@Autowired
private PmsBrandApi pmsBrandApi;
@Autowired
private TokenHolder tokenHolder;
@ApiOperation(value = "调用远程登录接口获取token")
@PostMapping(value = "/admin/login")
public CommonResult<LoginInfo> login(@RequestParam String username, @RequestParam String password) {
CommonResult<LoginInfo> result = umsAdminApi.login(username, password);
LoginInfo loginInfo = result.getData();
if (result.getData() != null) {
tokenHolder.putToken(loginInfo.getTokenHead() + " " + loginInfo.getToken());
}
return result;
}
@ApiOperation("调用远程接口分页查询品牌列表")
@GetMapping(value = "/brand/list")
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("页码") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每页数量") Integer pageSize) {
return pmsBrandApi.list(pageNum, pageSize);
}
@ApiOperation("调用远程接口获取指定id的品牌详情")
@GetMapping(value = "/brand/{id}")
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
return pmsBrandApi.detail(id);
}
@ApiOperation("调用远程接口添加品牌")
@PostMapping(value = "/brand/create")
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.create(pmsBrand);
}
@ApiOperation("调用远程接口更新指定id品牌信息")
@PostMapping(value = "/brand/update/{id}")
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.update(id,pmsBrand);
}
@ApiOperation("调用远程接口删除指定id的品牌")
@GetMapping(value = "/delete/{id}")
public CommonResult deleteBrand(@PathVariable("id") Long id) {
return pmsBrandApi.delete(id);
}
}
测试
下面我们通过 Postman 进行测试,首先调用登录接口获取到远程服务返回的 token 了;
再调用下需要登录认证的品牌列表接口,发现可以正常访问。
总结
Http Interface
让我们只需定义接口,无需定义方法实现就能进行远程 HTTP 调用,确实非常方便!但是其实现依赖 Webflux 的 WebClient,在我们使用 SpringMVC 时会造成一定的麻烦,如果能独立出来就更好了!
参考资料
官方文档:https://docs.spring.io/spring-framework/reference/integration/rest-clients.html
👇🏻 点击下方阅读原文,获取鱼皮往期编程干货。
往期推荐